/*
 * 多租户是 Orchard 的核心特性之一，即使你没有注意到，它也被广泛应用。
 * 一个租户本质上是一个拥有自己独立内容和配置的子站点，拥有自己的域名或 URL 前缀。
 * 你可以用租户来为多个客户在同一个 Orchard Core 应用中托管网站。
 * 这些站点彼此之间互不知晓，但它们都运行在同一个应用、同一份代码库上，并且可以访问相同的模块和主题。
 * 这使得维护此类站点无论是托管还是开发都非常高效。更多细节请查阅官方文档：https://docs.orchardcore.net/en/latest/docs/glossary/#tenant
 *
 * 如果你希望租户之间不是完全隔离的怎么办？如果有些内容或配置你希望在租户之间共享，或者有些功能你希望集中在一个租户上？
 * 你可以使用下面展示的 API 跨越租户边界，使用来自其他租户的任何服务！
 *
 * 现在我们进入了相当高级的领域，但别担心，我们会解释一切！
 */

using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.DependencyInjection;
using OrchardCore.ContentManagement;
using OrchardCore.Environment.Shell;
using System.Threading.Tasks;

namespace Ruitu.Edu.TrainingDemo.Controllers;

/// <summary>
/// 跨租户服务控制器 - 演示如何访问其他租户的服务
/// 
/// 主要用途：
/// - 演示如何在 Orchard Core 中跨租户访问服务
/// - 展示多租户架构下的服务调用
/// - 实现内容共享或集中化功能
/// 
/// 使用场景：
/// - 从一个租户访问另一个租户的内容项
/// - 在多租户环境下实现配置或数据共享
/// - 集中管理某些功能或数据
/// 
/// 设计原因：
/// - 利用 Orchard Core 的 Shell 容器机制
/// - 展示如何动态解析其他租户的依赖服务
/// - 实现灵活的多租户扩展能力
/// 
/// 设计模式：
/// - 依赖注入模式：通过 ServiceProvider 动态解析服务
/// - Shell 容器模式：利用 IShellHost 获取指定租户的服务作用域
/// </summary>
public sealed class CrossTenantServicesController : Controller
{
    private readonly IShellHost _shellHost;

    /// <summary>
    /// 构造函数 - 通过依赖注入初始化 ShellHost
    /// </summary>
    /// <param name="shellHost">ShellHost，用于获取其他租户的服务作用域</param>
    public CrossTenantServicesController(IShellHost shellHost) => _shellHost = shellHost;

    /// <summary>
    /// 跨租户内容项标题获取 - 演示如何访问其他租户的内容项
    /// 
    /// 你可以通过 /CrossTenantServices?contentItemId=ID 访问此接口。
    /// 其中 ID 需要是你在管理后台打开内容项时 URL 中看到的内容项 ID。
    /// 本示例默认访问 Default 租户，但你可以根据需要访问其他租户。
    /// </summary>
    /// <param name="contentItemId">内容项 ID</param>
    /// <returns>返回内容项的标题，如果未找到则返回 null</returns>
    [HttpGet, Route("CrossTenantServices")]
    public async Task<string> Index(string contentItemId)
    {
        // 如果 ModelState 无效，则中止操作并返回 null。
        if (!ModelState.IsValid) return null;

        // 即使你没有创建租户，Orchard 应用中也会有一个默认租户 Default。
        // 对于你创建的其他租户，可以提供其技术名称。

        // 在本例中，我们将访问 Default 租户的内容项，但当然这对任何租户都适用。
        // 你可以在应用中创建租户（启用 Tenants 功能并在配置/租户下创建），
        // 也可以在新租户上启用 Training Demo 并测试此功能。

        // 首先你需要获取租户的 shell 作用域，其中包含 shell 的 ServiceProvider。
        // 另外还有 IShellSettingsManager 服务可用于访问所有租户的 shell 设置（如技术名称和 URL）。
        var shellScope = await _shellHost.GetScopeAsync("Default");

        // 我们这里只返回内容项的标题，你也可以做其他操作，比如显示内容项。
        string title = null;

        // 使用 UsingAsync() 创建一个代码块，所有操作都在该租户的上下文中执行。
        // 这有点类似于控制器操作内部，但要记住所有这些都在 Default 租户上运行，
        // 即使你是在其他租户下访问的。
        await shellScope.UsingAsync(async scope =>
        {
            // 你可以从 shell 的 ServiceProvider 解析任何服务，这相当于动态依赖注入。
            var contentManager = scope.ServiceProvider.GetRequiredService<IContentManager>();

            // 可以像平常一样使用 IContentManager，这里为简化没有做错误处理或授权校验，实际使用时请补充。
            var contentItem = await contentManager.GetAsync(contentItemId);

            if (contentItem != null)
            {
                // DisplayText 就是你在 PersonPartHandler 中学到的内容项标题。
                title = contentItem.DisplayText;
            }
        });

        return title;
    }
}

// 训练部分结束：跨租户访问服务

// 下一站：Services/TestedService.cs
